home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’95 / NetFractal™ / Fractal 8 source / FractalEngine.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-24  |  16.0 KB  |  647 lines  |  [TEXT/MPS ]

  1. /*    
  2.     File:        FractalEngine.c
  3.  
  4.     Used to build:    “Fractal 8”
  5.     
  6.     Written by:        Jim Cathey            July 1985
  7.                     Eric Traut            November 1994
  8.  
  9.     Description:
  10.         The following code implements a “Fractal Contour” generating
  11.         program. See comments in the file “FractalMain.c” for more
  12.         information.
  13.         
  14.         The code in this file implements the fractal-generating
  15.         and plotting portion of the program.
  16. */
  17.  
  18.  
  19.  #pragma cplusplus off
  20. #include <Quickdraw.h>
  21. #include <ToolUtils.h>
  22. #include <Timer.h>
  23.  
  24. #include <stdlib.h>
  25.  
  26. #include "Fractal.h"
  27.  
  28. /* Functions defined within this file */
  29. static double RandomDouble(void);
  30. static void CalcXs(long len, long incr, long sk);
  31. static void CalcYs(long len, long incr, long sk);
  32. static void CalcDiagonals(long len, long incr, long sk);
  33. static long GetZValue(long x, long y);
  34. static long GetOldZValue(long x, long y);
  35. static void SetZValue(long d, long x, long y);
  36. static unsigned char GetLineColor(unsigned short red, unsigned short blue, unsigned short green);
  37. static void LineToOffscreen(long sX, long sY, long eX, long eY);
  38. static void LineXOffscreen(long startX, long startY, long endX, long endY);
  39. static void LineYOffscreen(long startX, long startY, long endX, long endY);
  40. static void PointOffscreen(long x, long y , unsigned char colorValue);
  41. static void SetGrayValues(void);
  42. static void Transform1Point(long xIndex, long yIndex, ThreePoint *resultPoint);
  43. static void WaterPlotLine(ThreePoint *startPoint, ThreePoint *endPoint);
  44.  
  45. /* Global variables */
  46. long             gMaxX, gMaxY;
  47. long            gLevel;                    /* Current level */
  48. unsigned char    gCurPixelColor;            /* Pixel value for offscreen line drawing */
  49. unsigned char    gDBluePixelColor;
  50. unsigned char    gLBluePixelColor;
  51. unsigned char    gBlackPixelColor;
  52. char*            gPixelBase;                /* Base of pixel store */
  53. long            gRowBytesIncrement;        /* Amount to increment each offscreen row */
  54. char            gGrayColors[kTotalGrays];/* Gray color table */
  55.  
  56. /* Macros */
  57. #define AbsoluteValue(v) ((v) >= 0 ? (v) : -(v))
  58. #define ConvertToShadeIndex(x) ((x >= kShadeMapSize) ? (kShadeMapSize - 1) : x)
  59.  
  60.  
  61. /*
  62.     RandomDouble
  63.     
  64.     This function returns a random number between -1 and 1.
  65. */
  66. double RandomDouble(void)
  67. {
  68.     return (double)rand() / RAND_MAX;
  69. }
  70.  
  71.  
  72. /*
  73.     CalcSurface
  74.     
  75.     This is the main surface-generating routine. It generates a random
  76.     fractal surface by filling in the gPointArray array.
  77. */
  78. void CalcSurface(unsigned short level)
  79. {
  80.     long             i, j, length, incrby, sk;
  81.     float             power;
  82.     UnsignedWide    startTime, endTime;
  83.  
  84.     if (gMorphFractal)
  85.         gMorphAmount = 0.0;
  86.     else
  87.         gMorphAmount = 1.0;
  88.     gLevel = level;
  89.  
  90.     gTotalFractals++;
  91.     gFractalChanged = true;
  92.     Microseconds(&startTime);
  93.     
  94.     gMaxX = 1 << level;
  95.     gMaxY = gMaxX / 2;
  96.     for (i = 0; i <= gMaxX; i++)           /* Clear the Array.  Use i & incrby as temps */
  97.         for (incrby = 0; incrby <= gMaxY; incrby++)
  98.             (*gNewPointArray)[i][incrby] = 0;
  99.  
  100.     for (i = 1; i <= level; i++) {
  101.         for (power = 1.0, j = 0; j < i; j++) {
  102.             if (gContourType == kStyleMountains)
  103.                 power *= 1.5 + (level / 40.0);    /* Mountains must be bumpier than the other styles */
  104.             else
  105.                 power *= 1.8 + (level / 40.0);
  106.         }
  107.         length = 10000 / power;            /* = 10000/(1.8^i) */
  108.         incrby = gMaxX / (1 << i);         /* # of line segments in a side of the triangle */
  109.         sk = incrby * 2;
  110.         CalcXs(length, incrby, sk);
  111.         CalcYs(length, incrby, sk);
  112.         CalcDiagonals(length, incrby, sk);
  113.     }
  114.     
  115.     Microseconds(&endTime);
  116.     if (startTime.hi == endTime.hi)
  117.         gMicrosecondCount += endTime.lo - startTime.lo;
  118.     else
  119.         gMicrosecondCount += startTime.lo - endTime.lo;
  120. }
  121.  
  122.  
  123. /*
  124.     CalcXs
  125.     
  126.     This function calculates the fractal values in the X direction.
  127.     It is called for each level and subdivides the existing edges
  128.     of the fractal to get the next level.
  129. */
  130. void CalcXs(long len, long incr, long sk)
  131. {
  132.     long             y, x;
  133.     long             d1, d2;
  134.  
  135.     for (y=0; y < gMaxX; y += sk) {
  136.         for (x = incr+y; x <= gMaxX; x += sk) {
  137.             d1 = GetZValue(x-incr, y);
  138.             d2 = GetZValue(x+incr, y);
  139.             SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
  140.         }
  141.     }
  142. }
  143.  
  144.  
  145. /*
  146.     CalcYs
  147.  
  148.     This function calculates the fractal values in the Y direction.
  149.     It is called for each level and subdivides the existing edges
  150.     of the fractal to get the next level.
  151. */
  152. void CalcYs(long len, long incr, long sk)
  153. {
  154.     long y, x;
  155.     long d1, d2;
  156.  
  157.     for (x = gMaxX; x >= 1; x -= sk)
  158.         for (y = incr; y <= x; y += sk) {
  159.             d1 = GetZValue(x, y + incr);
  160.             d2 = GetZValue(x, y - incr);
  161.             SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
  162.         }
  163. }
  164.  
  165.  
  166. /*
  167.     CalcDiagonals
  168.  
  169.     This function calculates the fractal values in the diagonal direction.
  170.     It is called for each level and subdivides the existing edges
  171.     of the fractal to get the next level.
  172. */
  173. void CalcDiagonals(long len, long incr, long sk)
  174. {
  175.     long             y, x;
  176.     long             d1, d2;
  177.         
  178.     for (x = 0; x < gMaxX; x += sk)
  179.     for (y = incr; y <= gMaxX - x; y += sk) {
  180.         d1 = GetZValue(x + y - incr, y - incr);
  181.         d2 = GetZValue(x + y + incr, y + incr);
  182.         SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x + y, y);
  183.     }
  184. }
  185.  
  186.  
  187. /*
  188.     GetZValue
  189.  
  190.     This function returns the Z value (i.e. the value stored in the
  191.     two-dimensional gPlotArray array. Because the fractal is triangular
  192.     and the array is square, we will store half the triange in the lower
  193.     part of the array the the other half in the upper part of the
  194.     array.
  195. */
  196. long GetZValue(long x, long y)
  197. {
  198.     if (y <= gMaxY)
  199.         return (*gNewPointArray)[x][y];
  200.     else
  201.         return (*gNewPointArray)[gMaxX - x][gMaxX + 1 - y];
  202. }
  203.  
  204.  
  205. /*
  206.     SetZValue
  207.  
  208.     This function saves the Z value (i.e. the value in the
  209.     two-dimensional gPlotArray array. Because the fractal is triangular
  210.     and the array is square, we will store half the triange in the lower
  211.     part of the array the the other half in the upper part of the
  212.     array.
  213. */
  214. void SetZValue(long d, long x, long y)
  215. {
  216.     if (y <= gMaxY)
  217.         (*gNewPointArray)[x][y] = d;
  218.     else
  219.         (*gNewPointArray)[gMaxX - x][gMaxX + 1 - y] = d;
  220. }
  221.  
  222.  
  223. /*
  224.     Transform1Point
  225.     
  226.     This function transforms a single XY point and stores
  227.     returns its XY coordinate as well as its scaled three-
  228.     space Z coordinate into the result structure.
  229. */
  230. void Transform1Point(long xIndex, long yIndex, ThreePoint *resultPoint)
  231. {
  232.     long    newComponent,
  233.             oldComponent;
  234.     long    componentShift;
  235.     long    xComponent, yComponent;
  236.     long    yResult, zResult;
  237.     
  238.     componentShift = kMaxLevel + 1 - gContourLevel;
  239.     xComponent = xIndex << componentShift;
  240.     yComponent = yIndex << componentShift;
  241.     
  242.     resultPoint->x = xComponent + yComponent + kXOrigin;
  243.     if (gContourType == kStyleMountains)
  244.         yResult = -xComponent + yComponent + kYMountainOrigin;
  245.     else
  246.         yResult = xComponent + -yComponent + kYOrigin;
  247.  
  248.     if (gMorphFractal) {
  249.         newComponent = (((yIndex <= gMaxY) ? (*gNewPointArray)[xIndex][yIndex] : 
  250.                 (*gNewPointArray)[gMaxX - xIndex][gMaxX + 1 - yIndex])) * gMorphAmount;
  251.     
  252.         oldComponent = (((yIndex <= gMaxY) ? (*gOldPointArray)[xIndex][yIndex] : 
  253.                 (*gOldPointArray)[gMaxX - xIndex][gMaxX + 1 - yIndex])) * gMorphAmountNeg;
  254.     
  255.         zResult = newComponent + oldComponent;
  256.     }
  257.     else {
  258.         zResult = (((yIndex <= gMaxY) ? (*gNewPointArray)[xIndex][yIndex] : 
  259.                 (*gNewPointArray)[gMaxX - xIndex][gMaxX + 1 - yIndex]));
  260.     }
  261.     
  262.     yResult -= (zResult >> kScaleShift);
  263.     resultPoint->y = yResult;
  264.     resultPoint->z = zResult;
  265. }
  266.  
  267.  
  268. /*
  269.     PlotData
  270.     
  271.     Our window is already open at this point, and it is cleared.
  272.     All we have to do now is fill it. This function draws the 
  273.     2D projection of the triangular database on the screen.
  274. */
  275. void PlotData(Boolean plotSurface)
  276. {
  277.     long             xindex, yindex;
  278.  
  279.     gMorphAmountNeg = (1.0 - gMorphAmount);
  280.     
  281.     if (plotSurface)
  282.         SetGrayValues();
  283.     
  284.     gPixelBase = (char*)GetPixBaseAddr(gOffscreenPixMap);
  285.     gRowBytesIncrement = (*gOffscreenPixMap)->rowBytes & 0x7FFF;
  286.  
  287.     gDBluePixelColor = GetLineColor(0, 0xFFFF, 0);
  288.     gLBluePixelColor = GetLineColor(0x3FFF, 0xFFFF, 0x3FFF);
  289.     gCurPixelColor = gBlackPixelColor = GetLineColor(0, 0, 0);
  290.  
  291.     if (!plotSurface) {
  292.         for (xindex = 1; xindex <= gMaxX; xindex++) {
  293.             for (yindex = 0; yindex <= xindex; yindex++)
  294.                 Transform1Point(xindex, yindex, &((*gPlotPointArray)[xindex][yindex]));
  295.         }
  296.  
  297.         for (xindex = 0; xindex < gMaxX; xindex++) {
  298.             for (yindex = xindex - 1; yindex >= 0; yindex--) {
  299.             
  300.                 ThreePoint     *currentPoint1,
  301.                             *currentPoint2,
  302.                             *currentPoint3;
  303.  
  304.                 currentPoint1 = &((*gPlotPointArray)[xindex][yindex]);
  305.                 currentPoint2 = &((*gPlotPointArray)[xindex + 1][yindex]);
  306.                 currentPoint3 = &((*gPlotPointArray)[xindex + 1][yindex + 1]);
  307.  
  308.                 if (gContourType == kStyleWater) {
  309.                     WaterPlotLine(currentPoint1, currentPoint2);
  310.                     WaterPlotLine(currentPoint2, currentPoint3);
  311.                     WaterPlotLine(currentPoint3, currentPoint1);
  312.                 }
  313.                 else {
  314.                     LineToOffscreen(currentPoint1->x, currentPoint1->y, currentPoint2->x, currentPoint2->y);
  315.                     LineToOffscreen(currentPoint2->x, currentPoint2->y, currentPoint3->x, currentPoint3->y);
  316.                     LineToOffscreen(currentPoint3->x, currentPoint3->y, currentPoint1->x, currentPoint1->y);
  317.                 }
  318.             }
  319.         
  320.         }
  321.     }
  322.     else {
  323.     
  324.         SetBlitBuffer((unsigned char *)gPixelBase, NULL, gRowBytesIncrement);
  325.     
  326.         for (xindex = 1; xindex <= gMaxX; xindex++) {
  327.             for (yindex = 0; yindex <= xindex; yindex++) {
  328.                 Transform1Point(xindex, yindex, &((*gPlotPointArray)[xindex][yindex]));
  329.             }
  330.         }
  331.  
  332.         for (xindex = 0; xindex < gMaxX; xindex++) {
  333.             for (yindex = xindex - 1; yindex >= 0; yindex--) {
  334.             
  335.                 ThreePoint        *currentPoint1,
  336.                                 *currentPoint2,
  337.                                 *currentPoint3;
  338.             
  339.                 long              vertex1H, vertex1V;
  340.                 long             vertex2H, vertex2V;
  341.                 long              vertex3H, vertex3V;
  342.                 long            diff1, diff2, diffTotal;
  343.                 unsigned char    color;
  344.                             
  345.                 currentPoint1 = &((*gPlotPointArray)[xindex][yindex]);
  346.                 
  347.                 vertex1H = currentPoint1->x;
  348.                 vertex1V = currentPoint1->y;
  349.                 
  350.                 currentPoint2 = &((*gPlotPointArray)[xindex + 1][yindex]);
  351.  
  352.                 vertex2H = currentPoint2->x;
  353.                 vertex2V = currentPoint2->y;
  354.  
  355.                 currentPoint3 = &((*gPlotPointArray)[xindex + 1][yindex + 1]);
  356.  
  357.                 vertex3H = currentPoint3->x;
  358.                 vertex3V = currentPoint3->y;
  359.                 
  360.                 color = 0;
  361.                 
  362.                 if (gContourType == kStyleWater) {
  363.                 
  364.                     if (currentPoint1->z <= 0) {
  365.                         vertex1V += currentPoint1->z >> kScaleShift;
  366.                         color = gDBluePixelColor;
  367.                     }
  368.                     
  369.                     if (currentPoint2->z <= 0) {
  370.                         vertex2V += currentPoint2->z >> kScaleShift;
  371.                         color = gDBluePixelColor;
  372.                     }
  373.  
  374.                     if (currentPoint3->z <= 0) {
  375.                         vertex3V += currentPoint3->z >> kScaleShift;
  376.                         color = gDBluePixelColor;
  377.                     }                
  378.                 
  379.                 }                
  380.         
  381.                 if (color != gDBluePixelColor) {
  382.  
  383.                     diff1 = currentPoint1->z - currentPoint2->z;
  384.                     if (diff1 < 0)
  385.                         diff1 = -diff1;
  386.                     
  387.                     diff2 = currentPoint3->z - currentPoint2->z;
  388.                     if (diff2 < 0)
  389.                         diff2 = -diff2;
  390.                     
  391.                     diffTotal = (diff1 + diff2) >> (kMaxLevel + 3 - gContourLevel);
  392.     
  393.                     color =  gGrayColors[gShadeMap[ConvertToShadeIndex(diffTotal)]];
  394.  
  395.                 }
  396.                                 
  397.                 FillTriangle(vertex1H, vertex1V,
  398.                              vertex2H, vertex2V,
  399.                              vertex3H, vertex3V,
  400.                              color);
  401.                 
  402.                 if (yindex < xindex - 1) {
  403.                 
  404.                     currentPoint2 = &((*gPlotPointArray)[xindex][yindex + 1]);
  405.                     
  406.                     vertex2H = currentPoint2->x;
  407.                     vertex2V = currentPoint2->y;
  408.  
  409.                     color = 0;
  410.  
  411.                     if (gContourType == kStyleWater) {
  412.                                             
  413.                         if (currentPoint1->z <= 0)
  414.                             color = gLBluePixelColor;
  415.  
  416.                         if (currentPoint2->z <= 0) {
  417.                             vertex2V += currentPoint2->z >> kScaleShift;
  418.                             color = gLBluePixelColor;
  419.                         }
  420.     
  421.                         if (currentPoint3->z <= 0)
  422.                             color = gLBluePixelColor;
  423.  
  424.                     }
  425.  
  426.                     if (color != gLBluePixelColor) {
  427.                     
  428.                         diff1 = currentPoint1->z - currentPoint2->z;
  429.                         if (diff1 < 0)
  430.                             diff1 = -diff1;
  431.                         
  432.                         diff2 = currentPoint3->z - currentPoint2->z;
  433.                         if (diff2 < 0)
  434.                             diff2 = -diff2;
  435.                         
  436.                         diffTotal = (diff1 + diff2) >> (kMaxLevel + 3 - gContourLevel);
  437.     
  438.                         color = gGrayColors[gShadeMap[ConvertToShadeIndex(diffTotal)]];
  439.                         
  440.                     }
  441.  
  442.                     FillTriangle(vertex1H, vertex1V,
  443.                              vertex2H, vertex2V,
  444.                              vertex3H, vertex3V,
  445.                              color);
  446.  
  447.                 }
  448.             }
  449.         }
  450.     }
  451. }
  452.  
  453.  
  454. /*
  455.     WaterPlotLine
  456. */
  457. void WaterPlotLine(ThreePoint *startPoint, ThreePoint  *endPoint)
  458. {
  459.     if (startPoint->z <= 0) {
  460.         PointOffscreen(startPoint->x, startPoint->y + (startPoint->z >> kScaleShift), gDBluePixelColor);
  461.         if (endPoint->z <= 0)
  462.             PointOffscreen(endPoint->x, endPoint->y + (endPoint->z >> kScaleShift), gDBluePixelColor);
  463.     }
  464.     else if (endPoint->z <= 0)
  465.         PointOffscreen(endPoint->x, endPoint->y + (endPoint->z >> kScaleShift), gDBluePixelColor);
  466.     else
  467.         LineToOffscreen(startPoint->x, startPoint->y, endPoint->x, endPoint->y);
  468. }
  469.  
  470.  
  471. /*
  472.     GetLineColor
  473.     
  474.     This function returns the pixel value for the given
  475.     RGB color.
  476. */
  477. unsigned char GetLineColor(unsigned short red, unsigned short blue, unsigned short green)
  478. {
  479.     RGBColor        curColor;
  480.  
  481.     curColor.red = red;
  482.     curColor.blue = blue;
  483.     curColor.green = green;
  484.     return Color2Index(&curColor);
  485. }
  486.  
  487.  
  488. /*
  489.     SetGrayValues
  490. */
  491. void SetGrayValues(void)
  492. {
  493.     long        curGrayIndex;
  494.     long        curGrayValue;
  495.     long        grayIncrement;
  496.     RGBColor    curColor;
  497.     
  498.     grayIncrement = (1<<15) / (kTotalGrays - 1);
  499.     curGrayValue = 32767;
  500.     for (curGrayIndex = 0; curGrayIndex < kTotalGrays; curGrayIndex++) {
  501.         curColor.red = curColor.blue = curColor.green = curGrayValue;
  502.         gGrayColors[curGrayIndex] = Color2Index(&curColor);
  503.         curGrayValue += grayIncrement;
  504.     }
  505. }
  506.  
  507.  
  508. /*
  509.     LineToOffscreen
  510.     
  511.     This function replaces the LineTo function with a custom
  512.     off-screen line drawing function.
  513. */
  514. void LineToOffscreen(long sX, long sY, long eX, long eY)
  515. {
  516.     if (AbsoluteValue(eY - sY) > AbsoluteValue(eX - sX)) {
  517.         if (eY < sY)
  518.             LineYOffscreen(eX, eY, sX, sY);
  519.         else
  520.             LineYOffscreen(sX, sY, eX, eY);
  521.     }
  522.     else {
  523.         if (eX < sX)
  524.             LineXOffscreen(eX, eY, sX, sY);
  525.         else
  526.             LineXOffscreen(sX, sY, eX, eY);
  527.     }
  528. }
  529.  
  530.  
  531. /*
  532.     LineXOffscreen
  533.  
  534.     Draws lines with a slope between -1 and 1
  535. */
  536. void LineXOffscreen(long startX, long startY, long endX, long endY)
  537. {
  538.     char*            pixelAddress;
  539.     unsigned char    localColor = gCurPixelColor;
  540.     long            localRowBytes = gRowBytesIncrement;
  541.     long            currentX;
  542.     
  543.     if (endX == startX) {
  544.         pixelAddress = gPixelBase + startX + (unsigned long)(startY * localRowBytes);
  545.         for (currentX = startX; currentX <= endX; currentX++)
  546.             *pixelAddress++ = localColor;    
  547.     }
  548.     else {
  549.         long            newY;
  550.         long            slope, currentY;
  551.         
  552.         slope = ((endY - startY) << 16) / (endX - startX);
  553.  
  554.         pixelAddress = gPixelBase + startX + (unsigned long)(startY * localRowBytes) - 1;
  555.         currentY = (startY << 16) + (1 << 15);
  556.         
  557.         if (endY > startY) {
  558.             for (currentX = startX; currentX <= endX; currentX++, currentY = newY) {
  559.                 newY = currentY + slope;
  560.                 *++pixelAddress = localColor;
  561.                 if ((newY >> 16) > (currentY >> 16))
  562.                     pixelAddress += localRowBytes;
  563.             }
  564.         }
  565.         else {
  566.             for (currentX = startX; currentX <= endX; currentX++, currentY = newY) {
  567.                 newY = currentY + slope;
  568.                 *++pixelAddress = localColor;
  569.                 if ((currentY >> 16) > (newY >> 16))
  570.                     pixelAddress -= localRowBytes;
  571.             }
  572.         }
  573.     }
  574. }
  575.  
  576.  
  577. /*
  578.     LineYOffscreen
  579.  
  580.     Draws lines with a slope <= -1 or >= 1
  581. */
  582. void LineYOffscreen(long startX, long startY, long endX, long endY)
  583. {
  584.     long            currentY;
  585.     char*            pixelAddress;
  586.     unsigned char    localColor = gCurPixelColor;
  587.     long            localRowBytes = gRowBytesIncrement;
  588.     
  589.     if (endY == startY) {
  590.         pixelAddress = gPixelBase + startX + (unsigned long)(startY * localRowBytes);
  591.         for (currentY = startY; currentY <= endY; currentY++) {
  592.             *pixelAddress = localColor;
  593.             pixelAddress += localRowBytes;
  594.         }
  595.     }
  596.     else {
  597.         long            newX;
  598.         long            slope, currentX;
  599.         
  600.         slope = ((endX - startX) << 16) / (endY - startY);
  601.  
  602.         pixelAddress = gPixelBase + startX + (unsigned long)(startY * localRowBytes);
  603.         currentX = (startX << 16) + (1 << 15);
  604.         
  605.         if (endX > startX) {
  606.             for (currentY = startY; currentY <= endY; currentY++, currentX = newX) {
  607.                 newX = currentX + slope;
  608.                 *pixelAddress = localColor;
  609.                 pixelAddress += localRowBytes;
  610.                 if ((newX >> 16) - (currentX >> 16))
  611.                     pixelAddress++;
  612.             }
  613.         }
  614.         else {
  615.             for (currentY = startY; currentY <= endY; currentY++, currentX = newX) {
  616.                 newX = currentX + slope;
  617.                 *pixelAddress = localColor;
  618.                 pixelAddress += localRowBytes;
  619.                 if ((currentX >> 16) - (newX >> 16))
  620.                     pixelAddress--;
  621.             }
  622.         }
  623.     }
  624. }
  625.  
  626.  
  627. /*
  628.     PointOffscreen
  629.  
  630.     Draws a single pixel
  631. */
  632. void PointOffscreen(long x, long y , unsigned char colorValue)
  633. {
  634.     char*            pixelAddress;
  635.  
  636.     pixelAddress = gPixelBase + x;
  637.     pixelAddress += (unsigned long)(y * gRowBytesIncrement);
  638.     *pixelAddress = colorValue;
  639. }
  640.  
  641.  
  642.  
  643.  
  644.  
  645.  
  646.  
  647.